﻿/***************************************************************
*       
* add by OceanHo 2015/8/25 17:52:08
*
****************************************************************/

using oceanho.webapi.factory.httpCore;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Web;
using System.Xml.Serialization;

namespace oceanho.webapi.factory.httpUtility
{



    /// <summary>
    /// 定义一个数据绑定测试实体对象类（User）
    /// </summary>
    public class User
    {
        public int id { get; set; }
        public uint age { get; set; }
        public string name { get; set; }
    }
    /// <summary>
    /// 定义一个工具类。用于解析请求参数与请求方法的参数构建与封装
    /// </summary>
    internal sealed class BinderUtility
    {
        /// <summary>
        /// 定义一个常量，该值表示request.params中，参数名称对于的参数最大限制值，在RequestUtility.GetReqMaxInexVal()方法中需要用到
        /// </summary>
        internal const int MAX_REQ_PARAM_VAL = 200;


        internal static MethodInfo _bindGenericMethod = typeof(
            BinderUtility).GetMethod("BindArrayAndGenericList", BindingFlags.Static | BindingFlags.NonPublic);

        /// <summary>
        /// 通过HttpRequest中的参数，将其封装成一个对象
        /// </summary>
        /// <param name="parameterName">参数名称</param>
        /// <param name="defaultValue">参数默认值</param>
        /// <param name="parameterType">参数类型</param>
        /// <param name="request">HttpRequest对象</param>
        /// <returns></returns>
        internal static object ResolverAsObject(string parameterName, object defaultValue, Type parameterType, HttpRequest request)
        {
            object objVal = defaultValue;
            // ValueType/String
            #region // ValueType/String

            if (parameterType.IsValueType || parameterType == typeof(string))
            {
                try
                {
                    objVal = request.Params[parameterName];
                    if (objVal != null)
                    {
                        objVal = Convert.ChangeType(objVal, parameterType);
                    }
                }
                catch
                {
                    objVal = defaultValue;
                }
                return objVal;
            }
            #endregion

            // Json进行反序列化
            #region // Json进行反序列化
            if (parameterName.StartsWith("_json_", StringComparison.OrdinalIgnoreCase))
            {
                try
                {
                    return Newtonsoft.Json.JsonConvert.DeserializeObject(request.Params[parameterName], parameterType);
                }
                catch
                {
                    try
                    {
                        if (defaultValue.GetType() == typeof(DBNull))
                        {
                            objVal = Activator.CreateInstance(parameterType);
                        }
                    }
                    catch
                    {
                        objVal = defaultValue;
                    }
                }
                return defaultValue;
            }
            #endregion

            // Xml 进行反序列化
            #region // Xml 进行反序列化

            if (parameterName.StartsWith("_xml_", StringComparison.OrdinalIgnoreCase))
            {
                try
                {
                    objVal = request.Params[parameterName];
                    if (objVal != null)
                    {

                        objVal = HttpUtility.HtmlDecode(objVal.ToString());
                        byte[] objBytes = System.Text.Encoding.UTF8.GetBytes(objVal.ToString());
                        using (MemoryStream ms = new MemoryStream())
                        {
                            ms.Position = 0;
                            ms.Write(objBytes, 0, objBytes.Length);
                            XmlSerializer xmlSerializer = new XmlSerializer(parameterType, string.Empty);
                            objVal = xmlSerializer.Deserialize(ms);
                        }

                        using (MemoryStream ms = new MemoryStream())
                        {
                            XmlSerializer xmlSerializer = new XmlSerializer(typeof(User), string.Empty);
                            ms.Position = 0;
                            xmlSerializer.Serialize(ms, new User() { age = 88, id = 88, name = "ss" });
                            StreamReader reader = new StreamReader(ms);
                            string xmls = reader.ReadToEnd();
                        }

                    }
                }
                catch
                {
                    try
                    {
                        if (defaultValue.GetType() != typeof(DBNull))
                        {
                            objVal = Activator.CreateInstance(parameterType);
                        }
                        else
                        {
                            objVal = defaultValue;
                        }
                    }
                    catch
                    {
                        objVal = defaultValue;
                    }
                    // throw new APIFW_BinderConvertException("xml转换失败,参数[" + parameterName + "]必须是[" + parameterType.ToString() + "]类型对象的xml格式字符串");
                }
                return objVal;
            }
            #endregion

            // 其它对象参数转换
            #region // 复杂类型参数绑定

            try
            {
                bool isbinded = false;

                objVal = CreateInstance(parameterName, parameterType, request, ref isbinded);

                if (isbinded)
                {
                    return objVal;
                }

                BindPropertyVal(ref objVal, request);
                return objVal;
            }
            catch
            {
                try
                {
                    if (defaultValue.GetType() == typeof(DBNull))
                    {
                        objVal = Activator.CreateInstance(parameterType);
                        defaultValue = objVal;
                    }
                }
                catch
                {
                }
                // throw new APIFW_BinderConvertException("请求参数转换为义务方法参数失败");
            }
            return defaultValue;
            #endregion
        }

        /// <summary>
        /// 根据参数名称，参数类型创建对应的对象实例（对数组，泛型数据类型的支持）
        /// </summary>
        /// <param name="parameterName">当前的绑定的参数的参数名称</param>
        /// <param name="parameterType">当前的参数的参数数据类型</param>
        /// <param name="request">当前httprequest对象</param>
        /// <param name="isbinded">是否已经绑定过了（可以理解为是一个输出参数）,如果此值为最后输出结果为true，表示此参数已经绑定过，不需要再绑定了</param>
        /// <returns></returns>
        private static object CreateInstance(string parameterName, Type parameterType, HttpRequest request, ref bool isbinded)
        {
            if (parameterType.IsArray || parameterType.IsGenericType)
            {
                isbinded = true;

                Type itemType = parameterType.IsArray
                    ? parameterType.GetElementType() : parameterType.GetGenericArguments()[0];

                MethodInfo _methodDo = _bindGenericMethod.MakeGenericMethod(itemType);
                return _methodDo.Invoke(null, new object[] { parameterName, request, parameterType.IsArray });
            }

            isbinded = false;
            return Activator.CreateInstance(parameterType);
        }

        /// <summary>
        /// 绑定数组与泛型
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="parameterName"></param>
        /// <param name="request"></param>
        /// <param name="isArray"></param>
        /// <returns></returns>
        internal static object BindArrayAndGenericList<T>(string parameterName, HttpRequest request, bool isArray)
        {
            object ret = null;
            Type tType = typeof(T);
            int mIndex = ReqResolverUtility.GetReqMaxIndexVal(request, parameterName);
            if (isArray)
            {
                if (mIndex >= 0)
                {
                    T[] result = new T[mIndex + 1];

                    if (tType == typeof(string) || tType.IsValueType)
                    {
                        for (int i = 0; i <= mIndex; i++)
                        {
                            result[i] = (T)GetValueTypeString(tType, request, parameterName + "[" + i + "]");
                        }
                    }
                    else
                    {
                        for (int i = 0; i <= mIndex; i++)
                        {
                            result[i] = Activator.CreateInstance<T>();
                            ret = result[i];
                            BindPropertyVal(ref ret, request, parameterName, i);
                            result[i] = (T)ret;
                        }
                    }
                    ret = result;
                }
            }
            else
            {
                if (mIndex >= 0)
                {
                    List<T> result = new List<T>(mIndex + 1);
                    if (tType == typeof(string) || tType.IsValueType)
                    {
                        for (int i = 0; i <= mIndex; i++)
                        {
                            result.Add(
                                (T)GetValueTypeString(tType, request, parameterName + "[" + i + "]"));
                        }
                    }
                    else
                    {
                        for (int i = 0; i <= mIndex; i++)
                        {
                            ret = Activator.CreateInstance<T>();
                            BindPropertyVal(ref ret, request, parameterName, i);
                            result.Add((T)ret);
                        }
                    }
                    ret = result;
                }
            }
            return ret;
        }

        /// <summary>
        /// 获取基础数据类型的默认值（对于string类型:返回string.empty，数值类型:返回0，对于char:返回'\0'，其它类型返回null）
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        internal static object GetTypeDv(Type type)
        {
            if (type == typeof(char)) { return '\0'; }
            if (type == typeof(string)) { return string.Empty; }

            if (type == typeof(byte) ||
                type == typeof(short) ||
                type == typeof(ushort) ||
                type == typeof(int) ||
                type == typeof(uint) ||
                type == typeof(long) ||
                type == typeof(ulong) ||
                type == typeof(decimal) ||
                type == typeof(float) ||
                type == typeof(double))
            { return Convert.ChangeType(0, type); }

            return null;
        }

        /// <summary>
        /// 解析并绑定属性值
        /// </summary>
        /// <param name="instance">绑定的对象实例</param>
        /// <param name="request">请求HttpRequest对象</param>
        /// <param name="prefix">绑定参数前缀（复杂类型时需要,比如参数是一个int的数组,参数名为 ars,就需要传ars过来）</param>
        /// <param name="index">绑定参数的索引（复杂类型时需要,比如参数是一个int的数组,第一项绑定就是0,第二项就是1）</param>
        internal static void BindPropertyVal(ref object instance, HttpRequest request, string prefix = null, int index = -1)
        {
            string reqK = string.Empty;
            string reqKey = (prefix != null && index >= 0) ? prefix + "[" + index + "]" : null;
            PropertyInfo[] properties = instance.GetType().GetProperties();
            foreach (PropertyInfo property in properties)
            {
                reqK = reqKey == null ?
                    property.Name : reqKey + "." + property.Name;

                // 值/String类型绑定
                if (BindValueTypeString(
                    ref instance,
                    property,
                    property.PropertyType, request, reqK))
                {
                    reqK = null;
                    continue;
                }

                // 字典/数组/泛型类型绑定
                if (BindArrayGenericType(
                    ref instance,
                    property,
                    property.PropertyType, request, reqK, index))
                {
                    reqK = null;
                    continue;
                }
            }
        }

        #region -- 辅助方法 -> BindValueTypeOrString --

        /// <summary>
        /// 将值绑定给instace对象（绑定前提：type.IsValueType 或者 type==typeof(string)）
        /// </summary>
        /// <param name="instance">绑定属性值的实例对象</param>
        /// <param name="property">绑定属性对象</param>
        /// <param name="propertyType">绑定属性数据类型</param>
        /// <param name="request">HttpRequest对象</param>
        /// <param name="reqKey">绑定属性的属性在HttpRequest中的参数名称</param>
        /// <returns></returns>
        private static bool BindValueTypeString(ref object instance, PropertyInfo property, Type propertyType, HttpRequest request, string reqKey)
        {
            if (propertyType.IsValueType || propertyType == typeof(string))
            {
                object value = request.Params[reqKey];
                try
                {
                    value = value == null
                        ? GetTypeDv(propertyType)
                        : Convert.ChangeType(value, propertyType);
                }
                catch
                {
                    value = GetTypeDv(propertyType);
                }
                finally
                {
                    try
                    {
                        property.SetValue(instance, value, null);
                    }
                    catch { }
                }
                return true;
            }
            return false;
        }

        /// <summary>
        /// 绑定参数为值类型/string类型的义务方法参数
        /// </summary>
        /// <param name="valType">值类型</param>
        /// <param name="request">HttpRequest对象</param>
        /// <param name="reqKey">绑定属性的属性在HttpRequest中的参数名称</param>
        /// <returns></returns>
        private static object GetValueTypeString(Type valType, HttpRequest request, string reqKey)
        {
            object value = request.Params[reqKey];
            try
            {
                value = value == null
                    ? GetTypeDv(valType)
                    : Convert.ChangeType(value, valType);
            }
            catch
            {
                value = GetTypeDv(valType);
            }
            finally
            {
            }
            return value;
        }
        #endregion

        #region -- 辅助方法 -> BindArrayGenericType --

        /// <summary>
        /// 
        /// </summary>
        /// <param name="instance"></param>
        /// <param name="property"></param>
        /// <param name="propertyType"></param>
        /// <param name="request"></param>
        /// <param name="prefix"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        private static bool BindArrayGenericType(ref object instance, PropertyInfo property, Type propertyType, HttpRequest request, string prefix = null, int index = -1)
        {
            object objVal;
            bool isbinded = false;
            objVal = CreateInstance(prefix, propertyType, request, ref isbinded);
            if (isbinded)
            {
                property.SetValue(instance, objVal, null);
            }
            return isbinded;
        }
        #endregion
    }
}
